package api

import (
	"fmt"
	"io/ioutil"
	pg "myutil/postgre"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/mholt/binding"
	"github.com/urfave/negroni"
)

// HTTPConstraint 请求入口设置约束条件，减少处理过程中数据传输量，提升处理效率
type HTTPConstraint struct {
	Fields string // 输出限定字段，查询时候使用，返回内容只限定fields内字段，传入时要转化为数据库对应字段
	//	Condition map[string]string // 输入过滤约束条件，只支持“＝”条件，未来考虑扩展到大于小于包含等运算
	//	Sort      int    // "1"代表升序；"-1"代表降序；0为空串无效
	SortField string // 排序的字段
	Limit     int    // 分页或输出条目概念，对应数据库语句为limit，对应API接口字段为limit
	Offset    int    // 偏移位置，结合limit实现分页功能
}

// Converter handler目录实现的api都需要将http内容尤其是form表单内容转化为结构体调用后端程序；
// 另外一方面，后端返回的结构体程序需要转化为json格式输出
type Converter interface {
	Request2struct(req *http.Request) (*pg.Constraint, error)
	Struct2Json(fields []string) ([]byte, error)
	Struct2SqlMap() map[string]interface{}
	HTTPFieldMap() map[string]interface{}
	Convert2Struct(model interface{}) (fk interface{}, err error)
	//	Convert2Sql(fk interface{}, model interface{}) error
	Convert2Sql(fk interface{}) interface{}
}

// FieldMap implement FieldMap interface
func (con *HTTPConstraint) FieldMap(req *http.Request) binding.FieldMap {
	return binding.FieldMap{
		&con.Fields:    "fields",
		&con.SortField: "sort",
		&con.Limit:     "limit",
		&con.Offset:    "offset",
	}
}

// ConvertConstraint 转换http请求到sql条件结构体
func (con *HTTPConstraint) ConvertConstraint(pgCon *pg.Constraint, convert map[string]string) {
	pgCon.Limit = con.Limit
	pgCon.Offset = con.Offset
	//	pgCon := &pg.Constraint{}
	// sort 处理 Fields还是 SortField? 数据还需要convert
	i, fd := parseSort(con.Fields)
	if i == 0 {
		return
	}
	pgCon.Sort = i
	pgCon.SortField = fd
	if con.Fields != "" {
		pgCon.Fields = convertFields(con.Fields, convert)
	}

	return
}

// parseSort parse to int and string
func parseSort(field string) (int, string) {
	fd := strings.TrimSpace(field)
	if fd == "" {
		return 0, ""
	}

	switch fd[0] {
	case '-':
		return -1, fd[1:]
	case '+':
		return 1, fd[1:]
	default:
		return 1, fd
	}
}

// convertFields 字段名称转换
func convertFields(fields string, convert map[string]string) []string {
	fds := strings.Split(fields, ",")
	len := len(fds)
	if len == 0 {
		return nil
	}
	for i, fd := range fds {
		fd1 := strings.TrimSpace(fd)
		/*
			if convert == nil {
				continue
			}
		*/
		if v, ok := convert[fd1]; ok {
			fds[i] = v
		} else {
			fds[i] = fd1
		}
	}
	return fds
}

// GETRequest 封装GET请求
func GETRequest(t *testing.T, form url.Values, handler http.HandlerFunc) {
	response := httptest.NewRecorder()

	//对form进行编码

	//	body := ioutil.NopCloser(strings.NewReader(form.Encode()))

	// Check file content behaviour
	url1 := make([]byte, 0, 100)
	url1 = append(url1, "http://localhost:3000/"...)
	var i int
	for k, v := range form {
		if i == 0 {
			url1 = append(url1, fmt.Sprintf("?%s=%s", k, v[0])...)
		} else {
			url1 = append(url1, fmt.Sprintf("&%s=%s", k, v[0])...)
		}
		i++
	}
	req, err := http.NewRequest("GET", string(url1), nil)
	if err != nil {
		t.Error(err)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
	//	req.Form = form
	req.ParseForm()

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)
	n := negroni.Classic() // Includes some default middlewares
	n.UseHandler(mux)

	n.ServeHTTP(response, req)
	fmt.Println(response.Body.String())
}

// POSTRequest 封装POST请求
func POSTRequest(t *testing.T, form url.Values, handler http.HandlerFunc) {
	response := httptest.NewRecorder()

	//对form进行编码

	body := ioutil.NopCloser(strings.NewReader(form.Encode()))

	// Check file content behaviour
	req, err := http.NewRequest("POST", "http://localhost:3000/", body)
	if err != nil {
		t.Error(err)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
	//	req.Form = form
	req.ParseForm()

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)
	n := negroni.Classic() // Includes some default middlewares
	n.UseHandler(mux)

	n.ServeHTTP(response, req)
	fmt.Println(response.Body.String())
}

// PUTRequest 封装PUT请求
func PUTRequest(t *testing.T, form url.Values, handler http.HandlerFunc) {
	response := httptest.NewRecorder()

	//对form进行编码

	body := ioutil.NopCloser(strings.NewReader(form.Encode()))

	// Check file content behaviour
	req, err := http.NewRequest("PATCH", "http://localhost:3000/", body)
	if err != nil {
		t.Error(err)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
	//	req.Form = form
	req.ParseForm()

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)
	n := negroni.Classic() // Includes some default middlewares
	n.UseHandler(mux)

	n.ServeHTTP(response, req)
	fmt.Println(response.Body.String())
}

// DELETERequest 封装DELETE请求
func DELETERequest(t *testing.T, form url.Values, handler http.HandlerFunc) {
	response := httptest.NewRecorder()

	//对form进行编码

	//	body := ioutil.NopCloser(strings.NewReader(form.Encode()))

	// Check file content behaviour
	url1 := make([]byte, 0, 100)
	url1 = append(url1, "http://localhost:3000/"...)
	var i int
	for k, v := range form {
		if i == 0 {
			url1 = append(url1, fmt.Sprintf("?%s=%s", k, v[0])...)
		} else {
			url1 = append(url1, fmt.Sprintf("&%s=%s", k, v[0])...)
		}
		i++
	}
	req, err := http.NewRequest("DELETE", string(url1), nil)
	if err != nil {
		t.Error(err)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
	//	req.Form = form
	req.ParseForm()

	mux := http.NewServeMux()
	mux.HandleFunc("/", handler)
	n := negroni.Classic() // Includes some default middlewares
	n.UseHandler(mux)

	n.ServeHTTP(response, req)
	fmt.Println(response.Body.String())
}
